/*
 * Copyright 2014-2026 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.wzq.util;

import java.io.IOException;
import java.lang.reflect.Array;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

import org.apache.http.HttpRequest;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestWrapper;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

/**
 * 基于apache httpClient的工具方法
 */
public class UtilHttp {
	@SuppressWarnings("deprecation")
	private static HttpRequest getOriginal(HttpRequest request) {
		if (request instanceof HttpRequestWrapper) {
			request = ((HttpRequestWrapper) request).getOriginal();
		} else if (request instanceof org.apache.http.impl.client.RequestWrapper) {
			request = ((org.apache.http.impl.client.RequestWrapper) request).getOriginal();
		} else {
			return request;
		}
		return getOriginal(request);
	}

	public static String execute(CloseableHttpClient httpClient, HttpContext httpContext, HttpUriRequest request) throws IOException {
		try (CloseableHttpResponse response = httpClient.execute(request, httpContext)) {
			StatusLine statusLine = response.getStatusLine();
			String html = EntityUtils.toString(response.getEntity());
			if (statusLine.getStatusCode() != 200) throw new IOException(statusLine + "\n" + html);
			return html;
		}
	}
	
	public static String post(CloseableHttpClient httpClient, HttpContext httpContext, String url, Map<String, Object> params) throws IOException {
		HttpPost post = new HttpPost(url);
		if (!params.isEmpty()) setParams(post, params);
		return execute(httpClient, null, post);
	}
	
	public static String post(CloseableHttpClient httpClient, HttpContext httpContext,
			String url, NameValuePair... params) throws IOException {
		HttpPost post = new HttpPost(url);
		if (params.length > 0) {
			post.setEntity(new UrlEncodedFormEntity(Arrays.asList(params), UtilIo.utf8));
		}
		return execute(httpClient, httpContext, post);
	}
	
	public static String post(CloseableHttpClient httpClient, HttpContext httpContext,
			String url, Object... params) throws IOException {
		if (params.length % 2 != 0) throw new IllegalArgumentException("提交的参数键值对，必须成对: " + Arrays.toString(params));
		
		HttpPost post = new HttpPost(url);
		if (params.length > 0) {
			List<NameValuePair> list = new ArrayList<>(params.length / 2);
			for (int i = 0; i < params.length;) {
				list.add(new BasicNameValuePair(
						UtilString.toString(params[i++], ""),
						UtilString.toString(params[i++], "")));
			}
			post.setEntity(new UrlEncodedFormEntity(list, UtilIo.utf8));
		}
		return execute(httpClient, httpContext, post);
	}

	public static void setParams(HttpPost post, Map<String, Object> map) {
		List<NameValuePair> list = new ArrayList<>(map.size());
		for (Entry<String, Object> entry : map.entrySet()) {
			list.add(new BasicNameValuePair(entry.getKey(), UtilString.toString(entry.getValue(), "")));
		}
		post.setEntity(new UrlEncodedFormEntity(list, UtilIo.utf8));
	}

    public static void addParameter(HttpPost post, Map<String, ?> params) throws IllegalArgumentException {
        if (params == null || params.isEmpty()) {
            return;
        }

        List<NameValuePair> list = new ArrayList<>(params.size());
        for (Entry<String, ?> entry : params.entrySet()) {
            String name = UtilString.toString(entry.getKey());
            Object value = entry.getValue();
            if (value == null) {
                list.add(new BasicNameValuePair(entry.getKey(), null));
            } else {
                if (value.getClass().isArray()) {
                    for (int len = Array.getLength(value), i = 0; i < len; i++) {
                        list.add(new BasicNameValuePair(name, UtilString.toString(Array.get(value, i))));
                    }
                } else if (value instanceof Collection) {
                    for (Iterator<?> iter = ((Collection<?>) value).iterator(); iter.hasNext();) {
                        list.add(new BasicNameValuePair(name, UtilString.toString(iter.next())));
                    }
                } else {
                    list.add(new BasicNameValuePair(name, UtilString.toString(value)));
                }
            }
        }
        post.setEntity(new UrlEncodedFormEntity(list, UtilIo.utf8));
    }
	
	public static void setParams(HttpPost post, List<NameValuePair> list ){
		post.setEntity(new UrlEncodedFormEntity(list, UtilIo.utf8));
	}
	
	public static List<NameValuePair> getPairList(HttpPost post, Map<String, Object> map) {
		List<NameValuePair> list = new ArrayList<>(map.size());
		for (Entry<String, Object> entry : map.entrySet()) {
			Object value = entry.getValue();
			if(value != null){
				if(value.getClass().isArray()){
					Object[] array = (Object[]) value;
					for(Object obj : array){
						list.add(new BasicNameValuePair(entry.getKey(), UtilString.toString(obj, "")));
					}
				}else{
					list.add(new BasicNameValuePair(entry.getKey(), UtilString.toString(entry.getValue(), "")));
				}
			}
		}
		return list;
	}

    public static <T> URIBuilder addParameter(URIBuilder builder, String name, T[] values) throws IllegalArgumentException {
        if (values == null || values.length == 0) {
            builder.addParameter(name, null);
            return builder;
        }

        for (T value : values) {
            builder.addParameter(name, UtilString.toString(value));
        }
        return builder;
    }
    public static URIBuilder addParameter(URIBuilder builder, Map<String, ?> params) throws IllegalArgumentException {
        if (params != null && !params.isEmpty()) {
            for (Entry<String, ?> entry : params.entrySet()) {
                String name = UtilString.toString(entry.getKey());
                Object value = entry.getValue();
                if (value == null) {
                    builder.addParameter(name, null);
                } else {
                    if (value.getClass().isArray()) {
                        for (int len = Array.getLength(value), i = 0; i < len; i++) {
                            builder.addParameter(name, UtilString.toString(Array.get(value, i)));
                        }
                    } else if (value instanceof Collection) {
                        for (Iterator<?> iter = ((Collection<?>) value).iterator(); iter.hasNext();) {
                            builder.addParameter(name, UtilString.toString(iter.next()));
                        }
                    } else {
                        builder.addParameter(name, UtilString.toString(value));
                    }
                }
            }
        }
        return builder;
    }

    /**
     * 构建URI
     * @param url 结果
     * @param params 参数
     * @return URI
     * @throws IllegalArgumentException 协议错误
     */
	public static URI toURI(String url, Map<String, Object> params) throws IllegalArgumentException {
		try {
			URIBuilder builder = new URIBuilder(url);
			for (Entry<String, Object> entry : params.entrySet()) {
			    builder.addParameter(entry.getKey(), UtilString.toString(entry.getValue(), ""));
			}
			return builder.build();
		} catch (URISyntaxException e) {
			throw new IllegalArgumentException(e);
		}
	}
	
	/** 参数按照自然顺序签名，如果是数组签名使用[name, v0, v1, v2] */
	public static String signParams(HttpRequest request) throws IOException {
		request = getOriginal(request);
		Map<String, List<String>> params = new TreeMap<>();
		if (request instanceof HttpPost) {
			HttpPost post = (HttpPost) request;
			for (NameValuePair nv : URLEncodedUtils.parse(post.getURI(), "UTF-8")) {
				UtilCollection.addIntoList(params, nv.getValue(), ArrayList.class, nv.getName());
			}
			for (NameValuePair nv : URLEncodedUtils.parse(post.getEntity())) {
				UtilCollection.addIntoList(params, nv.getValue(), ArrayList.class, nv.getName());
			}
		} else if (request instanceof HttpUriRequest) {
			HttpUriRequest uriRequest = (HttpUriRequest) request;
			for (NameValuePair nv : URLEncodedUtils.parse(uriRequest.getURI(), "UTF-8")) {
				UtilCollection.addIntoList(params, nv.getValue(), ArrayList.class, nv.getName());
			}
		}
		
		List<String> all = new ArrayList<>(params.size() + 4);
		for (Entry<String, List<String>> entry : params.entrySet()) {
			all.add(entry.getKey());
			for (String value : entry.getValue()) {
				all.add(value);
			}
		}
		return UtilString.md5(all);
	}

    /** 提取查询参数名 */
    public static Set<String> getNamesFromQueryString(String queryString) {
        Set<String> result = new LinkedHashSet<>();
        if (queryString == null || queryString.isEmpty()) {
            return result;
        }

        for (String nameValue : queryString.split("&")) {
            if (nameValue != null) {
                int pos = nameValue.indexOf('=');
                if (pos == -1) {
                    result.add(nameValue);
                } else {
                    result.add(nameValue.substring(0, pos));
                }
            }
        }
        return result;
    }

}
